#include "CElInputDevices.h"
#include "_CElInputDevices.cpp"

typedef struct _SECURITY_ATTRIBUTES
{
    unsigned long nLength;
    void * lpSecurityDescriptor;
    int bInheritHandle;
} * LPSECURITY_ATTRIBUTES;
EXTERN_C void * STDCALL CreateEventW(LPSECURITY_ATTRIBUTES, int, int, const wchar_t *);
EXTERN_C unsigned long STDCALL WaitForSingleObject(void*, unsigned long);
EXTERN_C bool STDCALL CloseHandle(void*);
EXTERN_C bool STDCALL SetEvent(void*);
EXTERN_C bool STDCALL ResetEvent(void*);

enum SimulatorType
{
    Simulator_None = 0,
    Simulator_Key,
    Simulator_Mouse,
    Simulator_Quit,
};

typedef struct SimulatorEventStructTag
{
    SimulatorType type;
    GrafixObjectEvent event;
} SimulatorEventStruct;

ECode CElInputDevices::Stop()
{
    ECode ec = NOERROR;
    if (m_isRunning) {
        stopDrawing();
        SimulatorEventStruct simulatorEvent;
        simulatorEvent.type = Simulator_Quit;
        ec = GetPipe();
        if (AURA_FAILED(ec)) {
            m_isRunning = false;
            m_channelId =-1;
            return NOERROR;
        }
        Int32 returnCode;
        Aura_ECode ec = AuraIpcChannel_SyncWrite(m_channelId,&simulatorEvent, sizeof(SimulatorEventStruct),&returnCode, sizeof(Int32));
        if (!AURA_FAILED(ec)) {
            AuraIpcChannel_Disconnect(m_channelId);
        }
        m_channelId =-1;
    }
    m_isRunning = false;
    return NOERROR;
}

ECode CElInputDevices::SendKeyEvent(
    /* [in] */ KeyEventType keyEvent,
    /* [in] */ Int32 keyCode,
    /* [in] */ Int64 timeStamp)
{
    //printf("CElInputDevices::SendKeyEvent keyCode=%d\n",keyCode);
    if(!m_isRunning) return NOERROR;
    if (keyEvent != GrafixObjectEventType_KeyUp
            && keyEvent != GrafixObjectEventType_KeyDown)
        return E_INVALID_ARGUMENT;
    SimulatorEventStruct simulatorEvent;
    simulatorEvent.type = Simulator_Key;
    simulatorEvent.event.keyValue = keyCode;
    simulatorEvent.event.modifier = InputMask_None;
    simulatorEvent.event.type = keyEvent;
    simulatorEvent.event.timestamp = timeStamp;

    WaitResult wr;
    m_pipeMutex->Lock(&wr);
    ECode ec = GetPipe();
    if (FAILED(ec)) {
        m_pipeMutex->Unlock();
        return ec;
    }
    Int32 returnCode;
    Aura_ECode ecode = AuraIpcChannel_SyncWrite(m_channelId,&simulatorEvent, sizeof(SimulatorEventStruct),&returnCode, sizeof(Int32));
    if (AURA_FAILED(ecode)) {
        m_pipeMutex->Unlock();
        return E_ACCESS_DENIED;
    }
    m_pipeMutex->Unlock();
    return NOERROR;
}

ECode CElInputDevices::SendMouseEvent(
    /* [in] */ MouseEventType mouseEvent,
    /* [in] */ Int32 x,
    /* [in] */ Int32 y,
    /* [in] */ Int64 timeStamp)
{
    //printf("CElInputDevices::SendMouseEvent\n");
    if(!m_isRunning) return NOERROR;
    //filter the moush down event,
    //because in sandalwood,widget cann't received mouse push event
    if(GrafixObjectEventType_StylusHeld == mouseEvent) return NOERROR;
    if (mouseEvent != GrafixObjectEventType_StylusPressed
            && mouseEvent != GrafixObjectEventType_StylusLifted
            && mouseEvent != GrafixObjectEventType_StylusMove
            && mouseEvent != GrafixObjectEventType_StylusReleased)
        return E_INVALID_ARGUMENT;
    SimulatorEventStruct simulatorEvent;
    simulatorEvent.type = Simulator_Mouse;
    simulatorEvent.event.modifier = InputMask_LButton;
    simulatorEvent.event.absoluteX = x;
    simulatorEvent.event.absoluteY = y;
    if (mouseEvent == GrafixObjectEventType_StylusReleased)
        simulatorEvent.event.type = GrafixObjectEventType_StylusLifted;
    else
        simulatorEvent.event.type = mouseEvent;
    simulatorEvent.event.timestamp = timeStamp;

    WaitResult wr;
    m_pipeMutex->Lock(&wr);
    ECode ec = GetPipe();
    if (FAILED(ec)) {
        m_pipeMutex->Unlock();
        return ec;
    }
    Int32 returnCode;
    Aura_ECode ecode = AuraIpcChannel_SyncWrite(m_channelId,&simulatorEvent, sizeof(SimulatorEventStruct),&returnCode, sizeof(Int32));
    if (AURA_FAILED(ecode)) {
        m_pipeMutex->Unlock();
        return E_ACCESS_DENIED;
    }
    m_pipeMutex->Unlock();
    return NOERROR;
}

ECode CElInputDevices::stopDrawing()
{
    //printf("CElInputDevices::stopDrawing\n\n");
    Aura_Handle memory;
    Aura_Address address;
    ECode ec = AuraSharedMemory_Create(
                     (Aura_PWChar)SIMULATOR_SHAREMEM_NAME,
                     0,
                     AURA_TRUE, AURA_TRUE,
                     &memory);
    if (FAILED(ec)) {
        printf("get sharememory failed\n");
        return E_ACCESS_DENIED;
    }
    ec = AuraSharedMemory_Attach(memory, Aura_MemoryProtection_RW,&address);
    if (FAILED(ec) || !address) {
        printf("attch sharememory failed\n");
        return E_ACCESS_DENIED;
    }
    SimulatorInfo* simulatorInfo = (SimulatorInfo*)address;
    simulatorInfo->simulatoHostStarted = false;
    AuraSharedMemory_Detach(memory, address);
    return ec;
}

ECode CElInputDevices::invokeCallBack()
{
    if (m_isRunning) {
        stopDrawing();
        Callback::SimulatorStoped();
        m_isRunning = false;
    }
    return NOERROR;
}

ECode ThreadWaitSimulatorStop(void *pArg)
{
    CElInputDevices* host = (CElInputDevices*)pArg;
    assert(host);
    void* hPalServerStartedEvent = CreateEventW(NULL, TRUE, FALSE, L"DeregisterElServices");
    if (WaitForSingleObject(hPalServerStartedEvent,(unsigned long)-1) == 0x00000102L) {
        fprintf(stderr, "[ERROR] CElInputDevices wait DeregisterElServices timeout.\n");
        CloseHandle(hPalServerStartedEvent);
        return E_TIMED_OUT;
    }
    CloseHandle(hPalServerStartedEvent);
    host->invokeCallBack();
    return NOERROR;
}

ECode CElInputDevices::startSimulator(char* product)
{
    if (_stricmp(product, "gk100")) {
    }
    return NOERROR;
}

ECode CElInputDevices::GetSimulatorWindowInfo(
    /* [out] */ struct SimulatorWindowInfo * pInfo)
{
    if(!m_isRunning) return E_INVALID_OPERATION;

    if (pInfo) {
        pInfo->mobileWidth = m_simulatorInfo.MobileWidth;
        pInfo->mobileHeight = m_simulatorInfo.MobileHeight;
        pInfo->mobilePosX = m_simulatorInfo.MobilePosX;
        pInfo->mobilePosY = m_simulatorInfo.MobilePosY;
        pInfo->lcdWidth = m_simulatorInfo.lcdWidth;
        pInfo->lcdHeight = m_simulatorInfo.lcdHeight;
        pInfo->lcdOffsetX = m_simulatorInfo.lcdOffsetX;
        pInfo->lcdOffsetY = m_simulatorInfo.lcdOffsetY;
        pInfo->pixleByte = m_simulatorInfo.pixleByte;
        pInfo->showLCDOnly = m_simulatorInfo.showLCDOnly;
    }
    return NOERROR;
}

ECode CElInputDevices::constructor()
{
    //printf("in Simulator mode\n");
    ECode ec = CMutex::New(&m_pipeMutex);
    if (FAILED(ec)) {
        m_pipeMutex = NULL;
        return ec;
    }
    m_channelId = -1;

    ec = CThread::New(ThreadWaitSimulatorStop, this, ThreadCreationFlag_Started,&m_pWaitThread);
    if (FAILED(ec)) {
        m_pWaitThread = NULL;
        return ec;
    }

    m_hSimulatorStartEvent = CreateEventW(
        NULL,         // default security attributes
        FALSE,         // manual-reset event
        FALSE,         // initial state is signaled
        L"SimulatorHostStarted"  // object name
    );
    if (!m_hSimulatorStartEvent) {
        return E_ACCESS_DENIED;
    }
    SetEvent(m_hSimulatorStartEvent);

    Aura_Handle memory;
    Aura_Address address;
    ec = AuraSharedMemory_Create((Aura_PWChar)SIMULATOR_SHAREMEM_NAME, 0, AURA_TRUE, AURA_TRUE,&memory);
    if (FAILED(ec)) {
        printf("get sharememory failed\n");
        return E_ACCESS_DENIED;
    }
    ec = AuraSharedMemory_Attach(memory, Aura_MemoryProtection_RW,&address);
    if (FAILED(ec) || !address) {
        printf("attch sharememory failed\n");
        return E_ACCESS_DENIED;
    }

    SimulatorInfo* simulatorInfo = (SimulatorInfo*)address;
    /*char* xdk_target_product = simulatorInfo->xdk_target_product;
    char* product = simulatorInfo->product;
    if (0 == _stricmp(xdk_target_product, "sandalwood")) {
        ec = startSimulator(product);
    }*/
    simulatorInfo->simulatoHostStarted = true;
    memcpy(&m_simulatorInfo, simulatorInfo, sizeof(SimulatorInfo));
    AuraSharedMemory_Detach(memory, address);
    m_isRunning = true;
    return ec;
}

ECode CElInputDevices::GetPipe()
{
    if (m_channelId < 0) {
        Aura_ECode ec = AuraIpcChannel_Connect((Aura_PWChar)L"EventDispatcher",&m_channelId);
        //printf("get pipe ec=%x\n",ec);
        if(AURA_FAILED(ec)) return E_ACCESS_DENIED;
    }
    return NOERROR;
}

CElInputDevices::~CElInputDevices()
{
    Stop();
    if (m_hSimulatorStartEvent) {
        ResetEvent(m_hSimulatorStartEvent);
        CloseHandle(m_hSimulatorStartEvent);
        m_hSimulatorStartEvent = NULL;
    }
    if (m_pipeMutex) {
        m_pipeMutex->Release();
        m_pipeMutex = NULL;
    }
    //if(m_pWaitThread)
    //{
    //    QuitCode quitCode;
    //    ECode ec=m_pWaitThread->GetQuitCode(&quitCode);
    //    if(!SUCCEEDED(ec))
    //    {
            //m_pWaitThread->Abort();
    //    }
    //}
}
